using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

using System.Threading;
using System.Diagnostics; // Only used for BlogLink_LinkClicked

namespace PictureMimic
{
	public partial class Main : Form
	{
		private Thread PrimaryEvolutionThread;
		private EvolutionHandler PrimaryEvolutionHandler;

		private DateTime StartTime;

		public Main()
		{
			InitializeComponent();
		}

		private void Main_Load(object sender, EventArgs e)
		{
			GUIRefreshTimer.Enabled = false;

			StartEvolutionButton.Enabled = false;
			StopEvolutionButton.Enabled = false;

			EvolutionaryConstraints.MaxPolygons = 50;
			MaxPolygons.Text = "50";
			MaxPolygons.CausesValidation = true;

			EvolutionaryConstraints.MaxPoints = 6;
			MaxPoints.Text = "9";
			MaxPoints.CausesValidation = true;

			EvolutionaryConstraints.BreederLimit = 10;
			BreederLimit.Text = "10";
			BreederLimit.CausesValidation = true;

			DominanceModel.Items.Clear();
			DominanceModel.Items.Add("Coin Flip");
			DominanceModel.Items.Add("Parental Fitness");
			DominanceModel.Items.Add("Mother Always");
			DominanceModel.Items.Add("Father Always");

			EvolutionaryConstraints.DominanceModel = new CoinFlipDominance();
			DominanceModel.SelectedIndex = 0;

			MutationModel.Items.Clear();
			MutationModel.Items.Add("None");
			MutationModel.Items.Add("Rampant");
			MutationModel.Items.Add("Coin Flip");
			MutationModel.Items.Add("Common");
			MutationModel.Items.Add("Inbreeding");

			EvolutionaryConstraints.MutationModel = new CommonMutation();
			MutationModel.SelectedIndex = 3;

			FitnessModel.Items.Add("Delta (Full)");
			FitnessModel.Items.Add("Delta (X-Step Quick Scan)");
			FitnessModel.Items.Add("Delta (Y-Step Quick Scan)");
			FitnessModel.Items.Add("Delta (Alternating Quick Scan)");
			FitnessModel.Items.Add("Compressed Delta");

			EvolutionaryConstraints.FitnessModel = new PixelDeltaQSAFitness();
			FitnessModel.SelectedIndex = 3;

			EvolutionParametersFrame.Enabled = false;
			EvolutionDetailsFrame.Enabled = false;
		}

		private void LoadSourceButton_Click(object sender, EventArgs e)
		{
			try
			{
				OpenFileDialog dialog = new OpenFileDialog();

				dialog.Filter = "Image Files(*.png; *.jpg; *.jpeg; *.gif; *.bmp)|*.png; *.jpg; *.jpeg; *.gif; *.bmp";

				if (dialog.ShowDialog() == DialogResult.OK)
				{
					SourceCanvas.Image = new Bitmap(dialog.FileName);
				}

				EvolutionaryConstraints.Environment = new Bitmap(SourceCanvas.Image);
				EvolutionaryConstraints.EnvironmentWidth = EvolutionaryConstraints.Environment.Width;
				EvolutionaryConstraints.EnvironmentHeight = EvolutionaryConstraints.Environment.Height;

				if (EvolutionaryConstraints.EnvironmentWidth > 255)
				{
					EvolutionaryConstraints.EnvironmentWidth = 255;
				}

				if (EvolutionaryConstraints.EnvironmentHeight > 255)
				{
					EvolutionaryConstraints.EnvironmentHeight = 255;
				}

				StartEvolutionButton.Enabled = true;
				StopEvolutionButton.Enabled = false;

				EvolutionParametersFrame.Enabled = true;
			}
			catch (Exception)
			{
				throw new ApplicationException("Failed to load source image. Sorry.");
			}
		}

		private void StartEvolutionButton_Click(object sender, EventArgs e)
		{
			CurrentGeneration.Text = "0";
			CurrentPopulation.Text = "0";
			ElapsedTime.Text = "00:00:00";

			EvolutionParametersFrame.Enabled = false;
			EvolutionDetailsFrame.Enabled = true;

			LoadSourceButton.Enabled = false;
			StartEvolutionButton.Enabled = false;
			StopEvolutionButton.Enabled = true;

			StartTime = DateTime.Now;

			PrimaryEvolutionHandler = new EvolutionHandler(new Bitmap(EvolutionaryConstraints.Environment));
			PrimaryEvolutionThread = new Thread(new ThreadStart(PrimaryEvolutionHandler.Start));

			try
			{
				PrimaryEvolutionThread.Start();
			}
			catch (Exception)
			{
				MessageBox.Show("Sorry, there was an error starting the primary thread. Shows over folks.");
			}

			GUIRefreshTimer.Enabled = true;
		}


		private void GUIRefreshTimer_Tick(object sender, EventArgs e)
		{
			if (!PrimaryEvolutionHandler.Running)
			{
				GUIRefreshTimer.Stop();
				return;
			}

			string timeDisplay;

			timeDisplay = String.Format("{0:T}", DateTime.Now.Subtract(StartTime));
			if (timeDisplay.IndexOf(".") > 0)
			{
				timeDisplay = timeDisplay.Substring(0, timeDisplay.IndexOf("."));
			}
			ElapsedTime.Text = timeDisplay;

			if (PrimaryEvolutionHandler.Running && !PrimaryEvolutionHandler.Busy)
			{
				CurrentGeneration.Text = PrimaryEvolutionHandler.Generations.ToString();
				CurrentPopulation.Text = PrimaryEvolutionHandler.Candidates.ToString();

				if (PrimaryEvolutionHandler.BestCandidate != null)
				{
					CandidateDNA bestCandidate = PrimaryEvolutionHandler.BestCandidate;

					CurrentGeneration.Text += " (" + bestCandidate.Generation.ToString() + ")";
					CurrentPopulation.Text += " (" + bestCandidate.Identity.ToString() + ")";

					TargetCanvas.Image = bestCandidate.GetBitmap();
				}
			}
		}

		private void StopEvolutionButton_Click(object sender, EventArgs e)
		{
			GUIRefreshTimer.Enabled = false;

			if (PrimaryEvolutionHandler != null && PrimaryEvolutionThread != null)
			{
				if (PrimaryEvolutionHandler.Running)
				{
					PrimaryEvolutionHandler.Stop();
				}
			}

			EvolutionParametersFrame.Enabled = true;
			EvolutionDetailsFrame.Enabled = false;

			LoadSourceButton.Enabled = true;
			StartEvolutionButton.Enabled = true;
			StopEvolutionButton.Enabled = false;
		}

		private void Main_FormClosing(object sender, FormClosingEventArgs e)
		{
			// Attempt to clean up. Just in case people didn't stop the evolution.

			if (PrimaryEvolutionHandler != null)
			{
				if (PrimaryEvolutionHandler.Running)
				{
					PrimaryEvolutionHandler.Stop();
				}
			}
		}

		private void BlogLink_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
		{
			ProcessStartInfo info = new ProcessStartInfo("http://www.matthewlynch.net/");
			Process.Start(info);
		}

		private void MaxPolygons_Validating(object sender, CancelEventArgs e)
		{
			int polygons;
			bool passed = true;

			if (passed = Int32.TryParse(MaxPolygons.Text, out polygons))
			{
				if (passed = (polygons >= 2 && polygons <= 1000))
				{
					EvolutionaryConstraints.MaxPolygons = polygons;
					MaxPolygons.BackColor = Color.White;
					StartEvolutionButton.Enabled = true;
				}
			}

			if (!passed)
			{
				StartEvolutionButton.Enabled = false;
				MaxPolygons.BackColor = Color.LightPink;
			}
		}

		private void MaxPoints_Validating(object sender, CancelEventArgs e)
		{
			int points;
			bool passed = true;

			if (passed = Int32.TryParse(MaxPoints.Text, out points))
			{
				if (passed = (points >= 3 && points <= 30))
				{
					EvolutionaryConstraints.MaxPoints = points;
					MaxPoints.BackColor = Color.White;
					StartEvolutionButton.Enabled = true;
				}
			}

			if (!passed)
			{
				StartEvolutionButton.Enabled = false;
				MaxPoints.BackColor = Color.LightPink;
			}
		}

		private void BreederLimit_Validating(object sender, CancelEventArgs e)
		{
			int breeders;
			bool passed = true;

			if (passed = Int32.TryParse(BreederLimit.Text, out breeders))
			{
				if (passed = (breeders >= 2 && breeders <= 100))
				{
					EvolutionaryConstraints.BreederLimit = (ushort)breeders;
					BreederLimit.BackColor = Color.White;
					StartEvolutionButton.Enabled = true;
				}
			}

			if (!passed)
			{
				StartEvolutionButton.Enabled = false;
				BreederLimit.BackColor = Color.LightPink;
			}
		}

		private void FitnessModel_SelectedIndexChanged(object sender, EventArgs e)
		{
			switch (FitnessModel.SelectedItem.ToString())
			{
				case "Delta (Full)":
					EvolutionaryConstraints.FitnessModel = new PixelDeltaFitness();
					break;
				case "Delta (X-Step Quick Scan)":
					EvolutionaryConstraints.FitnessModel = new PixelDeltaQSXFitness();
					break;
				case "Delta (Y-Step Quick Scan)":
					EvolutionaryConstraints.FitnessModel = new PixelDeltaQSYFitness();
					break;
				case "Delta (Alternating Quick Scan)":
					EvolutionaryConstraints.FitnessModel = new PixelDeltaQSAFitness();
					break;
				case "Compressed Delta":
					EvolutionaryConstraints.FitnessModel = new CompressedDeltaFitness();
					break;
				default:
					EvolutionaryConstraints.FitnessModel = new PixelDeltaFitness();
					break;
			}
		}

		private void MutationModel_SelectedIndexChanged(object sender, EventArgs e)
		{
			switch (MutationModel.SelectedItem.ToString())
			{
				case "None":
					EvolutionaryConstraints.MutationModel = new NoMutation();
					break;
				case "Rampant":
					EvolutionaryConstraints.MutationModel = new RampantMutation();
					break;
				case "Coin Flip":
					EvolutionaryConstraints.MutationModel = new CoinFlipMutation();
					break;
				case "Common":
					EvolutionaryConstraints.MutationModel = new CommonMutation();
					break;
				case "Inbreeding":
					EvolutionaryConstraints.MutationModel = new InbredMutation();
					break;
				default:
					EvolutionaryConstraints.MutationModel = new RampantMutation();
					break;
			}
		}

		private void DominanceModel_SelectedIndexChanged(object sender, EventArgs e)
		{
			switch (DominanceModel.SelectedItem.ToString())
			{
				case "Coin Flip":
					EvolutionaryConstraints.DominanceModel = new CoinFlipDominance();
					break;
				case "Parental Fitness":
					EvolutionaryConstraints.DominanceModel = new FitnessDominance();
					break;
				case "Mother Always":
					EvolutionaryConstraints.DominanceModel = new MaternalDominance();
					break;
				case "Father Always":
					EvolutionaryConstraints.DominanceModel = new PaternalDominance();
					break;
				default:
					EvolutionaryConstraints.DominanceModel = new FitnessDominance();
					break;
			}
		}
	}
}